package com.wepa.smackgcp;

import org.apache.log4j.Logger;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.Base64;

import com.wepa.smackgcp.oauth2.GoogleOAuth2SASLMechanism;

public class JobNotifier {

    private static final Logger logger = Logger.getLogger(JobNotifier.class);

    private XMPPConnection      xmppConnection;
    private PacketCollector     jobCollector;

    public void initialize(String googleId, String accessToken, String resourceName) throws Exception {
	try {
	    ConnectionConfiguration connConfig = new ConnectionConfiguration("gmail.com");
	    connConfig.setRosterLoadedAtLogin(false);
	    connConfig.setSendPresence(false);
	    connConfig.setReconnectionAllowed(false);

	    logger.debug("Constructing an XMPPConnection with provided configuration");
	    xmppConnection = new XMPPConnection(connConfig);
	    // register the a method responsible for disconnecting the connection before the JVM shuts down
	    Runtime.getRuntime().addShutdownHook(getGoogleXMPPConnectionShutdownThread(xmppConnection));

	    // add support for OAuth2 during the authentication phase of XMPP
	    logger.debug("Registering OAuth2 support");
	    SASLAuthentication.registerSASLMechanism("X-OAUTH2", GoogleOAuth2SASLMechanism.class);
	    SASLAuthentication.supportSASLMechanism("X-OAUTH2", 0);

	    logger.debug("Connecting...");
	    xmppConnection.connect();
	    logger.debug("Logging in with X-OAUTH2 token under resource name '" + resourceName + "'");
	    xmppConnection.login("na", accessToken, resourceName);
	    logger.debug("SUCCESS!");

	    // subscribe to Google CloudPrint job notifications
	    logger.debug("Subscribing to Google cloud print notifications with Google talk id " + googleId);
	    xmppConnection.sendPacket(new CloudPrintSubscriptionIQ(googleId));
	    // TODO: need error checking to make sure the subscription was successful

	    // setup a packet collector to hold job messages as they come in
	    logger.debug("Creating a packet collector to receive/store print job notifications as they come in");
	    jobCollector = xmppConnection.createPacketCollector(new MessageTypeFilter(Type.normal));
	} catch (XMPPException e) {
	    throw new Exception("Failed to establish XMPP print job notifications with Google", e);
	}
	logger.info("Ready.");
    }

    /**
     * Blocks the current thread until a "QUEUED" job notification is sent from Google. The printerID associated with
     * the notification sent is decoded and returned.
     * 
     * @return
     */
    public String waitForAJob() {
	assert jobCollector != null;

	logger.info("");
	logger.info("Waiting for the next print job packet...");
	Packet p = jobCollector.nextResult();
	logger.debug("Got one!");
	logger.debug(p.toXML());

	DefaultPacketExtension extension = (DefaultPacketExtension) p.getExtension("push", "google:push");
	String printerId = new String(Base64.decode((String) extension.getValue("data")));
	logger.info("Packet notification is for printer " + printerId);

	return printerId;
    }

    private Thread getGoogleXMPPConnectionShutdownThread(final Connection conn) {
	assert conn != null;

	return new Thread("GoogleXMPPConnectionShutdownThread") {
	    @Override
	    public void run() {
		logger.info("Disconnecting XMPP connection to Google");
		conn.disconnect();
	    }
	};
    }
}
